Langchain4j - 提示词工程、多模态、以及 Tool Calling
提示词工程介绍
在 LangChain4j 的低阶与高阶 API 中,提示词工程被高度结构化地抽象为了以 ChatMessage 为核心的消息流模型。这与市面上大多数原生大模型(如 OpenAI 结构、Anthropic 结构)的 Chat Completions 协议实现了完美对接。理解了提示词这套结构,就掌握了控制大模型灵魂的方向盘。
核心四大消息元组:在大模型的多轮对话和提示词设计中,LangChain4j 提炼出了四个最基础的核心消息类,它们全部继承自 ChatMessage 接口:


例如 UserMessage:
1 | UserMessage msg = UserMessage.builder() |
对于常见的提示词,可以参考 DeepSeek prompt-library,这里有很多已经写好的提示词模板可被使用。
提示词常见使用套路
演示项目依赖
关于父项目POM,可以参考 Langchain4j - 基础工程 - 父项目 POM。
1 | <dependencies> |
配置文件
application.yml
1 | server: |
基本套路演示
定义一个 PromptTestService:
1 | import dev.langchain4j.data.message.ChatMessage; |
业务演示类:
1 | /** |
上面像玩法二这种形式的接口,如果参数很多的额话,也可以使用下面封装成实体参数的形式:
1 |
|
其实上面这种 @V 或者 @StructuredPrompt 形式的提示词映射,底层的都是依靠 PromptTemplate 和 Prompt 两个类实现的:
1 | // ... |
1 | [main] INFO dev.langchain4j.http.client.log.LoggingHttpClient -- HTTP request: |
图像多模态
案例一:OCR 识别
1 | import dev.langchain4j.data.message.ImageContent; |
案例二:智能图片鉴别助手
1 |
|
音频和视频多模态
下面 是一个简单的案例:
1 | import dev.langchain4j.data.message.AudioContent; |
整合第三方多模态大模型
父工程中依赖管理:
1 | <dependency> |
在子项目引入依赖:
1 | <!-- langchain4j 整合第三方平台 --> |
注入大模型 Model(以阿里百炼平台万象模型为例):
1 |
|
业务测试类:
1 | import dev.langchain4j.community.model.dashscope.WanxImageModel; |
提示万象模型生成一张 ”古典美女图片“:

透传自定义提示词参数
这里以 enable_search 为例进行说明。enable_search 不是 OpenAI 标准字段,是阿里云百炼的扩展参数,LangChain4j 的 OpenAiChatModel 需要通过 customParameters 透传。这样所有相关的全局请求自动带 enable_search,Controller 不用改。
1 | public static void test01() { |
1 | [main] INFO dev.langchain4j.http.client.log.LoggingHttpClient -- HTTP request: |
或者也可以实现局部方法的透传:
1 |
|
Tool Calling
低阶API调用案例
定义一个大模型助手接口(这里以获取开票为例)
1 | public interface FunctionAssistant { |
使用低阶 API 生成上述接口的代理实现(其中定义好):
1 | // https://docs.langchain4j.dev/tutorials/tools#low-level-tool-api |
业务测试类:
1 |
|
调用过程分析
我们请求这个接口,来看一下整个 tool calling 的调用全过程。整个过程分成两大回合、四个步骤:
第一回合:用户发起诉求,大模型听懂意图,掏出回调工具
- 发起请求:投喂“用户诉求 + 工具说明书”。微服务向 DashScope 阿里百炼平台发送了第一个 POST 请求。请求体中除了包含用户的业务大白话外,最核心的是携带了一个 tools 数组。
- 模型判定:大模型没有直接回答前端,它的 finish_reason 是 tool_calls,它认为当前必须先调用工具。生成了唯一的调用 ID call_5d829929fa2c4d6382c78787,并将解析好的结构化 JSON 字符串参数作为 arguments 吐回给本地的微服务系统。
1 | 2026-04-10T11:41:27.783+08:00 INFO 37824 --- [oundedElastic-1] d.l.http.client.log.LoggingHttpClient : HTTP request: |
间歇期:本地微服务业务层默默干活。在我们的日志中,下面两行代表了 LangChain4j 在本地反射调用我们编写的业务代码:
1 | call_5d829929fa2c4d6382c78787 |
此时你的 Spring 容器找到了对应的 @Tool 或 toolExecutor 本地方法,将大模型给的参数传进去,在本地数据库或第三方开票系统走完了逻辑,并得到了一个返回值:”开票成功”。
第二回合:把执行结果交给大模型结案。 再次请求:带着 “全量历史 + 工具执行结果” 二次叩门。这是因为大模型是无状态的。为了让它知道工具执行得怎么样,LangChain4j 必须把刚才所有的对话连同工具的执行结果全量打包再次发送给模型。 注意看这次请求的 messages 列表,像接龙一样包含了 3 个角色:
- role: user —— 用户的原始输入。
- role: assistant —— 大模型上一轮要求调用工具的指令(包含 tool_calls)。
- role: tool —— 核心补充!携带对应的 tool_call_id,告诉模型:你刚才要我执行的工具,我已经执行完了,结果是 “开票成功”。
大模型收到工具返回的 “开票成功” 之后,进入最后一轮思考。用户在最开始说过一句:“最终返回json格式数据”。大模型在 reasoning_content 里敏锐地抓到了这一点。它没有返回普通的 “发票已开好”,而是为了满足用户的任性要求,用 Markdown 的 json 代码块,自己组装并输出了规范的 JSON 字符串:
1 | 2026-04-10T11:41:35.689+08:00 INFO 37824 --- [oundedElastic-1] d.l.http.client.log.LoggingHttpClient : HTTP request: |
你日志中第二回合发送请求时,消息体(messages)里的第三段,就是标准的 ToolExecutionResultMessage(在早期版本或部分低阶 API 中也被称为 ToolResultMessage)。
1 | { |
更加优化的高阶写法
定义一个 LLM 的回调工具类:
1 | import dev.langchain4j.agent.tool.P; |
修改注入的代码:
1 | // https://docs.langchain4j.dev/tutorials/tools#high-level-tool-api |